﻿using System;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nature.Toolkit;
using Nature.Data;


namespace Nature.Toolkit.GMix
{
    class ExpressionInfo
    {        
        #region Private Fields
        string[] m_speciesIDs;
        string[] m_componentNames;
        string[] m_parameterNames;
        CompositionObject[] m_speciesCompositions;
        Dictionary<string, int> m_speciesIndexes;
        Dictionary<string, int> m_componentIndexes;
        Dictionary<string, int> m_parameterIndexes;
        IRhsEnumerable[] m_rhsEnumerations;
        ComponentInfo[] m_components;
        ComponentInfo[] m_resolutionSequence;
        CHOFormula[] m_speciesCHOFormulas;        
        #endregion

        #region Constructor
        internal ExpressionInfo(IExpression expression, System.Collections.IEnumerable entitiesHeap, MixSetupInfo setupInfo)
        {
            this.SourceContract = expression;
            this.SetupInfo = (setupInfo != null ? setupInfo.Clone() : new MixSetupInfo() );

            var speciesIDs =
                (from c in expression.Components
                 from lhs in c.LhsSet.OfType<ILhsSpecies>()
                 select lhs.SpeciesId.ToLower()).Distinct();

            DataSet = new ThermoDataSet(entitiesHeap, speciesIDs);


            int indexC = DataSet.ElementIds.IndexOf(i => i.ToLower() == "c");
            int indexH = DataSet.ElementIds.IndexOf(i => i.ToLower() == "h");
            int indexO = DataSet.ElementIds.IndexOf(i => i.ToLower() == "o");

            if (DataSet.Nsp == 0)
            {
                throw new ArgumentException("the expression object does not refers to any chemical species", "expression");
            }
            m_speciesIDs = DataSet.SpeciesIds.Select(i => i.ToLower()).ToArray();
            m_speciesIndexes = m_speciesIDs.ToDictionary(i => i, i => Array.IndexOf(m_speciesIDs, i));
            m_speciesCompositions = new CompositionObject[m_speciesIDs.Length];
            m_speciesCHOFormulas = new CHOFormula[m_speciesIDs.Length];
            

            for (int isp = 0; isp < m_speciesCompositions.Length; ++isp)
            {
                double contentC = (indexC < 0 ? 0.0 : DataSet.Spel[isp][indexC]);
                double contentH = (indexH < 0 ? 0.0 : DataSet.Spel[isp][indexH]);
                double contentO = (indexO < 0 ? 0.0 : DataSet.Spel[isp][indexO]);
                m_speciesCHOFormulas[isp] = new CHOFormula(contentC, contentH, contentO);
            }


            for (int isp = 0; isp < m_speciesCompositions.Length; ++isp)
            {
                CompositionObject comp = new CompositionObject(this, m_speciesIDs[isp]);
                double[] x = new double[DataSet.Nsp];
                x[isp]  = 1.0;
                comp.X = x;                
                m_speciesCompositions[isp] = comp;
            }

            m_rhsEnumerations =
                (from cmp in expression.Components
                 from lhs in cmp.LhsSet
                 let rhs = cmp[lhs] as IRhsEnumerable
                 where rhs != null
                 select rhs).ToArray();

            m_parameterNames = m_rhsEnumerations.Select(i => i.ParameterName.ToLower()).ToArray();
            m_parameterIndexes = m_parameterNames.ToDictionary(i => i, i => Array.IndexOf(m_parameterNames, i));


            m_componentNames = expression.Components.Select(i => i.ComponentName.ToLower()).ToArray();
            m_componentIndexes = m_componentNames.ToDictionary(i => i, i => Array.IndexOf(m_componentNames, i));
            m_components = expression.Components.Select(i => new ComponentInfo(i, this)).ToArray();
            foreach (var c in m_components)
            {
                c.FixRelations();
            }

            if (m_componentNames.Any(i => m_speciesIDs.Contains(i)))
            {
                throw new ArgumentException("one or more complex component id is identical to the pure species id", "expression");
            }

            if (m_components.Length == 0)
            {
                throw new ArgumentException("the expression object does not contain any component defined", "expression");
            }

            MixExecutionPlan execPlan = new MixExecutionPlan(this);
            m_resolutionSequence = execPlan.GetComponentsResolutionSequence();

            this.SpeciesArrayObjectPool = ObjectPool<double[]>.CreateObjectPool(() => new double[Nsp]);
            this.ObjectPoolContainer = new ObjectPoolContainer();
            //this.ObjectPoolContainer.Register<EntropyConservationLawTemperatureSolver>(() => new EntropyConservationLawTemperatureSolver());
            this.ObjectPoolContainer.Register<EnthalpyConservationLawSolver>(() => new EnthalpyConservationLawSolver());
            this.ObjectPoolContainer.Register<ShockWaveSoneSolver>(()=> new ShockWaveSoneSolver());
            this.ObjectPoolContainer.Register<IsentropicCompressionSoneSolver>(() => new IsentropicCompressionSoneSolver());
            this.ObjectPoolContainer.Register<ShockCompressionVector>(()=> ShockCompressionVector.Create());
            this.ObjectPoolContainer.Register<IsentropicCompressionVector>(() => IsentropicCompressionVector.Create());

        } 
        #endregion

        #region Properties

        /// <summary>
        /// Expression source contract
        /// </summary>
        internal IExpression SourceContract { get; private set; }

        internal MixSetupInfo SetupInfo { get; private set; }

        /// <summary>
        /// Number of species
        /// </summary>
        internal int Nsp
        {
            get { return m_speciesIDs.Length; }
        }

        internal IDataSet DataSet { get; private set; }

        internal ReadOnlyArray<string> SpeciesIDs { get { return m_speciesIDs; } }

        internal ReadOnlyArray<IRhsEnumerable> Parameters { get { return m_rhsEnumerations; } }

        internal int SpeciesIndex(string speciesID) { return m_speciesIndexes[speciesID]; }

        internal int ComponentIndex(string componentName) { return m_componentIndexes[componentName]; }

        internal int ParameterIndex(string parameterName) { return m_parameterIndexes[parameterName]; }

        internal ReadOnlyArray<string> ParameterNames { get { return m_parameterNames; } }

        internal ReadOnlyArray<string> ComponentNames { get { return m_componentNames; } }        

        internal ReadOnlyArray<ComponentInfo> Components { get { return m_components; } }

        internal ReadOnlyArray<CHOFormula> SpeciesCHOFormulas { get { return m_speciesCHOFormulas; } }

        internal ReadOnlyArray<CompositionObject> SpeciesCompositions { get { return m_speciesCompositions; } }

        internal ObjectPoolContainer ObjectPoolContainer { get; private set; }

        internal IObjectPool<double[]> SpeciesArrayObjectPool { get; private set; }

        #endregion

        internal void Fill(MixProjection projection)
        {
            foreach (ComponentInfo info in m_resolutionSequence)
            {
                info.Fill(projection);
            }
        }

        internal void CalcEnthalpies(double[] h, double temperature)
        {
            DataSet.CalcEnthalpies(h, temperature);
        }

        internal void CalcEntropies(double[] s, double temperature)
        {
            DataSet.CalcEntropies(s, temperature);
        }

        internal void CalcCpHeatCapacities(double[] cp, double temperature)
        {
            DataSet.CalcCpHeatCapacities(cp, temperature);
        }
    }
}
